//
//  HealthManager.swift
//  HealthLibDemo
//
//  Created by youmy on 2021/2/25.
//

import Foundation
import HealthKit

class HealthManager {

    static let healthStore = HKHealthStore()
    static let readTypes = Set([
        HKObjectType.quantityType(forIdentifier: .stepCount), // 步数
        HKObjectType.quantityType(forIdentifier: .activeEnergyBurned), // 能量消耗
        HKObjectType.quantityType(forIdentifier: .distanceCycling), // 骑行距离
        HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning), // 步行+跑步距离
        HKObjectType.quantityType(forIdentifier: .heartRate), // 心率
        HKObjectType.quantityType(forIdentifier: .restingHeartRate), // 静息心率
        HKObjectType.categoryType(forIdentifier: .sleepAnalysis) // 睡眠分析
    ])

    static let shareTypes = Set([
        HKObjectType.quantityType(forIdentifier: .stepCount),
        HKObjectType.quantityType(forIdentifier: .activeEnergyBurned),
        HKObjectType.quantityType(forIdentifier: .distanceCycling),
        HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning),
        HKObjectType.quantityType(forIdentifier: .heartRate),
        HKObjectType.quantityType(forIdentifier: .restingHeartRate),
        HKObjectType.categoryType(forIdentifier: .sleepAnalysis)
    ])
}

// MARK: - Auth
extension HealthManager {

    static func requestAuth() {
        guard HKHealthStore.isHealthDataAvailable() else {
            print("hk >> HealthKit不可以使用")
            return
        }

        requestAuthToReadDatas()
        requestAuthToWriteData()
    }

    /// 请求权限，读取数据
    static func requestAuthToReadDatas() {
        healthStore.requestAuthorization(toShare: readTypes as? Set<HKSampleType>, read: nil) { success, error in
            if success {
                print("hk >> 请求数据读取权限成功 --- Apple Health")
            } else {
                print("hk >> 请求数据读取权限失败 ---> \(String(describing: error?.localizedDescription))")
            }
        }
    }

    /// 请求权限，保存数据（写入数据）
    static func requestAuthToWriteData() {
        healthStore.requestAuthorization(toShare: shareTypes as? Set<HKSampleType>, read: nil) { success, error in
            if success {
                print("hk >> 请求数据写入权限成功 --- Apple Health")
            } else {
                print("hk >> 请求数据读取权限 ---> \(String(describing: error?.localizedDescription))")
            }
        }
    }
}

extension HealthManager {
    /// 写入健康数据到苹果健康
    static func write(tuples: [(Double, Date, Date)], unit: HKUnit, identifier: HKQuantityTypeIdentifier) {

        var des = ""

        switch identifier {
        case .stepCount:
            des = "准备写入步数"
        case .activeEnergyBurned:
            des = "准备写入能量"
        case .distanceWalkingRunning:
            des = "准备写入(步行+跑步距离)"
        case .distanceCycling:
            des = "骑行距离"
        case .heartRate:
            des = "心率"
        case .restingHeartRate:
            des = "静息心率"
        default:
            break
        }

        print("hk >> 写入: \(des)")

        let sampleList = tuples.compactMap { (tuple) -> HKQuantitySample? in
            // 写入的数据
            let countUnitQuantity = HKQuantity(unit: unit, doubleValue: tuple.0)
            var sample: HKQuantitySample?
            if let countUnitType = HKQuantityType.quantityType(forIdentifier: identifier) {
                sample = HKQuantitySample(type: countUnitType, quantity: countUnitQuantity, start: tuple.1, end: tuple.2)
            }
            return sample
        }

        if sampleList.isEmpty { return }

        healthStore.save(sampleList) { (success, error) in
            if success {
                print("hk >> save success")
            } else {
                print("hk >> error ---> \(String(describing: error))")
            }
        }
    }

    // 获取最近一次写入数据的时间
    static func getStartDate(_ identifier: HKQuantityTypeIdentifier, completion: @escaping (Date, HKQuantitySample?) -> Void) {

        if let queryType = HKSampleType.quantityType(forIdentifier: identifier) {
            getMostRecentSample(for: queryType) { (samples, error) in
                if let sample = samples?.first {
                    let date = sample.startDate
                    completion(date, sample)
                } else {
                    completion(getOneDate(), nil)
                }
            }
        }
    }

    // 获取最近一次写入睡眠数据的时间
    static func getRecentSleep(_ identifier: HKCategoryTypeIdentifier, completion: @escaping (Date, HKCategorySample?) -> Void) {

        if let sleepType = HKSampleType.categoryType(forIdentifier: identifier) {

            // Use a sortDescriptor to get the recent data first
            let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)

            // we create our query with a block completion to execute
            let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 1, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in

                if error != nil {
                    completion(getOneDate(), nil)
                    return
                }

                if let result = tmpResult, let sample = result.first as? HKCategorySample {
                    let date = sample.startDate
                    completion(date, sample)
                } else {
                    completion(getOneDate(), nil)
                }
            }

            healthStore.execute(query)
        }
    }

    // 获取最近写入苹果健康的数据
    static func getMostRecentSample(for sampleType: HKSampleType, completion: @escaping ([HKQuantitySample]?, Error?) -> Swift.Void) {

        //1. Use HKQuery to load the most recent samples.
        let mostRecentPredicate = HKQuery.predicateForSamples(withStart: Date.distantPast,
                                                              end: Date(),
                                                              options: .strictStartDate)

        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate,
                                              ascending: false)

        let limit = 1

        let sampleQuery = HKSampleQuery(sampleType: sampleType,
                                        predicate: mostRecentPredicate,
                                        limit: limit,
                                        sortDescriptors: [sortDescriptor]) { (query, samples, error) in

            //2. Always dispatch to the main thread when complete.
            DispatchQueue.main.async {

                guard let samples = samples as? [HKQuantitySample] else {

                    completion(nil, error)
                    return
                }

                // 过滤掉不是当前软件的
                let arr = samples.filter({$0.sourceRevision.source.bundleIdentifier == Bundle.main.bundleIdentifier})

                completion(arr, nil)
            }
        }

        HKHealthStore().execute(sampleQuery)
    }

    /// 获取0点日期
    static func getOneDate(date: Date = Date()) -> Date {

        let calendar = Calendar(identifier: Calendar.Identifier.gregorian)

        var dataCom = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date())

        dataCom.hour = 0
        dataCom.minute = 0
        dataCom.second = 0

        guard let res = calendar.date(from: dataCom) else { return Date() }

        return res
    }
}
